/*
 * FileClient - Flash client for file transfers
 *
 * Copyright 2008 James W. Hofmann
 *
 *
 * This code is in the public domain and may be used as-is or for
 * your own programs.
 * 
 */
package basicfiletransfer;

class FileClient implements PeerApi {

	public static var client : FileClient;

	var peer : Peer;
	public var indata : StringBuf;
	public var outdata : String;
	var bufferstart : Int;
	var bufferend : Int;
	
	var scnx : haxe.remoting.SocketConnection;
	var s : flash.net.XMLSocket;

	public var status : Int;
	
	public var updatecallback : String->Void;

	public var parser : RequestParser;
	
	public static inline var status_disconnected = 0;
	public static inline var status_transferring = 1;
	public static inline var status_success = 2;

	public static inline var bytestotransfer = 3000;

	public static var inst : PeerApi;
		
	public function new(domainname : String, portnum : Int, 
						updatecallback : String->Void,
						parser : RequestParser) 
	{
		this.parser = parser;
		status = status_transferring;
		this.updatecallback = updatecallback;
		updatecallback(
			"Connecting to "+domainname+":"+Std.string(portnum));
		s = new flash.net.XMLSocket();
		s.addEventListener(flash.events.Event.CONNECT, onConnect,
			false, 1, true);
		s.addEventListener(flash.events.Event.CLOSE, close,
			false, 1, true);
		s.addEventListener(flash.events.IOErrorEvent.IO_ERROR, ioerror,
			false, 1, true);
		s.addEventListener(
						flash.events.SecurityErrorEvent.SECURITY_ERROR, 
							securityerror,
			false, 1, true);
		s.connect(domainname, portnum);
		scnx = haxe.remoting.SocketConnection.socketConnect(s);
		peer = new Peer(scnx.peer);
		inst = this;
	}

	public function onConnect(e : flash.events.Event)
	{
		updatecallback("Connected");
	}

	public function close(e : flash.events.Event)
	{
		status = status_disconnected;
		updatecallback("Connection closed by foreign host");
	}

	public function ioerror(e : flash.events.IOErrorEvent)
	{
		status = status_disconnected;
		updatecallback("Connection closed by foreign host");
	}

	public function securityerror(e : flash.events.IOErrorEvent)
	{
		status = status_disconnected;
		updatecallback("Security error");
	}

	public function pull( data : String, lastpart : Bool )
	{
		updatecallback("Pulling byte "+Std.string(bufferstart));
		if (indata==null)
		{
			indata = new StringBuf();
			indata.add(data);
		}
		else
		{
			indata.add(data);
		}
		if (lastpart)
		{
			updatecallback("Downloaded "+
							Std.string(bufferend)+" bytes");
			transactioncomplete();
		}
		else
		{
			peer.push();
		}
	}

	public function push()
	{
		updatecallback("Pushing byte "+Std.string(bufferstart));
		try
		{
		if (bufferend<outdata.length)
		{
			bufferend+=bytestotransfer;
			peer.pull(outdata.substr(bufferstart,bytestotransfer),false);
			bufferstart+=bytestotransfer;
		}
		else
		{
			peer.pull(outdata.substr(bufferstart),true);		
		}
		}
		catch(d : Dynamic) // sometimes flash barfs here and
						   // it's not clear why. Easier to just
						   // restart...
		{
			status = status_disconnected;
			updatecallback("Push error");
		}
	}
	
	public function uploadrequest(requestcode : Int, requestcontent :
									Dynamic)
	{
		outdata = 
			parser.parse(requestcode, requestcontent);
		beginupload();
	}

	function beginupload()
	{
		bufferstart = 0;
		bufferend = 0;
		push();		
	}

	public function transactioncomplete()
	{
		if (!(status==status_success))
		{
			status = status_success;
			peer.transactioncomplete();
			updatecallback("Finished.");
		}
	}
	
	public function downloadrequest(requestcode : Int, requestcontent :
									Dynamic)
	{
		peer.uploadrequest(requestcode, requestcontent);
	}

	public static function 
	ofConnection( scnx : haxe.remoting.SocketConnection ) : FileClient 
	{
		return (cast scnx).__private;
	}

}

// define a typed remoting API
class Peer extends haxe.remoting.AsyncProxy<PeerApi> {
}
